Completed
Push — master ( 576d64...22e707 )
by Sander
01:42 queued 33s
created

background.js ➔ ... ➔ PAPI.createCredential   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
    API.runtime.onConnect.addListener(function (port) {
10
11
        port.onMessage.addListener(function (msg) {
12
            if (msg === 'credential_amount') {
13
                port.postMessage('credential_amount:' + local_credentials.length);
14
            }
15
16
        });
17
18
    });
19
20
    var master_password = null;
21
22
    function getMasterPasswordSet() {
23
        return (master_password !== null);
24
    }
25
26
    _self.getMasterPasswordSet = getMasterPasswordSet;
27
28
    function setMasterPassword(opts) {
29
        master_password = opts.password;
30
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
31
            // Save the password in plain text on user request.
32
            // No secure local storage is available :/
33
            storage.set('master_password', opts.password);
34
        } else {
35
            storage.set('master_password', null);
36
        }
37
38
        if (opts.password) {
39
            getSettings();
40
        } else {
41
            displayLogoutIcons();
42
        }
43
44
    }
45
46
    _self.setMasterPassword = setMasterPassword;
47
48
49
    var testMasterPasswordAgainst;
50
51
    function isMasterPasswordValid(password) {
52
        //return true;
53
        try {
54
            PAPI.decryptString(testMasterPasswordAgainst, password);
55
            return true;
56
        } catch (e) {
57
            return false;
58
        }
59
    }
60
61
    _self.isMasterPasswordValid = isMasterPasswordValid;
62
63
64
    var local_credentials = [];
65
    var local_vault = [];
66
    var encryptedFieldSettings = ['default_vault', 'nextcloud_host', 'nextcloud_username', 'nextcloud_password', 'vault_password'];
67
    _self.settings = {};
68
    _self.ticker = null;
69
    _self.running = false;
70
    function getSettings() {
71
72
        storage.get('settings').then(function (_settings) {
73
74
            if (!_settings || !_settings.hasOwnProperty('nextcloud_host')) {
75
                return;
76
            }
77
78
            if (!master_password && _settings.hasOwnProperty('nextcloud_username') && _settings.hasOwnProperty('vault_password')) {
79
                _self.settings.isInstalled = 1;
80
                testMasterPasswordAgainst = _settings.nextcloud_username;
81
                return;
82
            }
83
84
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
85
                var field = encryptedFieldSettings[i];
86
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
87
            }
88
89
90
            _self.settings = _settings;
91
92
            if (!_self.settings.hasOwnProperty('ignored_sites')) {
93
                _self.settings.ignored_sites = [];
94
            }
95
96
            PAPI.host = _settings.nextcloud_host;
97
            PAPI.username = _settings.nextcloud_username;
98
            PAPI.password = _settings.nextcloud_password;
99
            if (!_settings.vault_password) {
100
                return;
101
            }
102
            if (PAPI.credentialsSet()) {
103
                getCredentials();
104
                if (_self.running) {
105
                    clearInterval(_self.ticker);
106
                }
107
108
                _self.running = true;
109
                _self.ticker = setInterval(function () {
110
                    getCredentials();
111
                }, _self.settings.refreshTime * 1000);
112
            } else {
113
                console.log('Login details are missing!');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
114
            }
115
        });
116
    }
117
118
    _self.getSettings = getSettings;
119
120
    function getRuntimeSettings() {
121
        return _self.settings;
122
    }
123
124
    _self.getRuntimeSettings = getRuntimeSettings;
125
126
    function getSetting(name) {
127
        return _self.settings[name];
128
    }
129
130
    _self.getSetting = getSetting;
131
132
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
133
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
134
            var field = encryptedFieldSettings[i];
135
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
136
        }
137
        PAPI.host = settings.nextcloud_host;
138
        PAPI.username = settings.nextcloud_username;
139
        PAPI.password = settings.nextcloud_password;
140
141
        if (!settings.hasOwnProperty('ignored_sites')) {
142
            settings.ignored_sites = [];
143
        }
144
145
        //window.settings contains the run-time settings
146
        _self.settings = settings;
147
148
149
        storage.set('settings', settings).then(function () {
150
            getSettings();
151
        });
152
153
    }
154
155
    _self.saveSettings = saveSettings;
156
157
158
    function getCredentials() {
159
        //console.log('Loading vault with the following settings: ', settings);
160
        var tmpList = [];
161
        PAPI.getVault(_self.settings.default_vault.guid, function (vault) {
162
            if (vault.hasOwnProperty('error')) {
163
                return;
164
            }
165
            var _credentials = vault.credentials;
166
            for (var i = 0; i < _credentials.length; i++) {
167
                var key = _self.settings.vault_password;
168
                var credential = _credentials[i];
169
                if (credential.hidden === 1) {
170
                    continue;
171
                }
172
                var usedKey = key;
173
                //Shared credentials are not implemented yet
174
                if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
175
                    usedKey = PAPI.decryptString(credential.shared_key, key);
176
177
                }
178
                credential = PAPI.decryptCredential(credential, usedKey);
179
                if (credential.delete_time === 0) {
180
                    tmpList.push(credential);
181
                }
182
183
            }
184
            delete vault.credentials;
185
            local_vault = vault;
186
            local_credentials = tmpList;
187
            updateTabsIcon();
188
        });
189
    }
190
191
    _self.getCredentials = getCredentials;
192
193
    function getCredentialByGuid(guid) {
194
        for (var i = 0; i < local_credentials.length; i++) {
195
            var credential = local_credentials[i];
196
            if (credential.guid === guid) {
197
                return credential;
198
            }
199
        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
200
    }
201
202
    _self.getCredentialByGuid = getCredentialByGuid;
203
204
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
205
        if (!master_password) {
206
            return [];
207
        }
208
        if (!_url || _url === '') {
209
            return [];
210
        }
211
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
212
        var found_list = [];
213
        for (var i = 0; i < local_credentials.length; i++) {
214
            if (local_credentials[i].url && local_credentials[i].username && local_credentials[i].password) {
215
                if (local_credentials[i].url.indexOf(url) !== -1) {
216
                    found_list.push(local_credentials[i]);
217
                }
218
            }
219
        }
220
        return found_list;
221
    }
222
223
    _self.getCredentialsByUrl = getCredentialsByUrl;
224
225
226
    function getCredentialForHTTPAuth(req) {
227
        return getCredentialsByUrl(req.url)[0];
228
    }
229
230
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
231
232
    var mined_data = [];
233
234
    function minedForm(data, sender) {
235
        var url = sender.url;
236
        var existingLogins = getCredentialsByUrl(sender.url);
237
        var title = API.i18n.getMessage('detected_new_login') + ':';
238
        var minedMatchingID = null;
239
        for (var j = 0; j < existingLogins.length; j++) {
240
            var login = existingLogins[j];
241
            if (login.username === data.username) {
242
                if (login.password !== data.password) {
243
                    minedMatchingID = login.guid;
244
                    title = API.i18n.getMessage('detected_changed_login') + ':';
245
                }
246
                else {
247
                    //console.log('No changes detected');
248
                    delete mined_data[sender.tab.id];
249
                    return;
250
                }
251
            }
252
        }
253
        mined_data[sender.tab.id] = {
254
            title: title,
255
            url: url,
256
            username: data.username,
257
            password: data.password,
258
            label: sender.title,
259
            guid: minedMatchingID
260
        };
261
262
        //console.log('Done mining, ', mined_data, sender.tab.id);
263
    }
264
265
    _self.minedForm = minedForm;
266
267
    function getMinedData(args, sender) {
268
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
269
        var senderUrl = sender.tab.url;
270
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
271
        if(!_self.settings.hasOwnProperty('ignored_sites')){
272
            return mined_data[sender.tab.id];
273
        }
274
        var matches = _self.settings.ignored_sites.filter(function (item) {
275
            return typeof item === 'string' && site.indexOf(item) > -1;
276
        });
277
278
        if (matches.length !== 0) {
279
            return null;
280
        }
281
        return mined_data[sender.tab.id];
282
    }
283
284
    _self.getMinedData = getMinedData;
285
286
    function clearMined(args, sender) {
287
        delete mined_data[sender.tab.id];
288
    }
289
290
    _self.clearMined = clearMined;
291
292
    function saveMinedCallback(args) {
293
        createIconForTab(args.sender.tab);
294
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
295
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
296
            });
297
        });
298
    }
299
300
    function ignoreSite(_url) {
301
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
302
            _self.settings.ignored_sites = [];
303
        }
304
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
305
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
306
            _self.settings.ignored_sites.push(site);
307
            saveSettings(_self.settings);
308
        }
309
    }
310
311
    _self.ignoreSite = ignoreSite;
312
313
    function passToParent(args, sender) {
314
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
315
        });
316
    }
317
318
    _self.passToParent = passToParent;
319
320
    function getActiveTab(opt) {
321
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
322
            var tab = tabs[0];
323
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
324
            });
325
        });
326
    }
327
328
    _self.getActiveTab = getActiveTab;
329
330
    function updateCredentialUrlDoorhanger(login) {
331
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
332
            var tab = tabs[0];
333
            var data = login;
334
            data.url = tab.url;
335
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
336
            API.tabs.sendMessage(tab.id, {
337
                method: 'showUrlUpdateDoorhanger',
338
                args: {data: data}
339
            });
340
        });
341
    }
342
343
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
344
345
    function updateCredentialUrl(data, sender) {
346
        mined_data[sender.tab.id] = data;
347
        saveMined({}, sender);
348
349
    }
350
351
    _self.updateCredentialUrl = updateCredentialUrl;
352
353
    function saveMined(args, sender) {
354
        var data = mined_data[sender.tab.id];
355
        var credential,
356
            credential_index;
357
358
        if (data.guid === null) {
359
            credential = PAPI.newCredential();
360
        } else {
361
            for (var i = 0; i < local_credentials.length; i++) {
362
                if (local_credentials[i].guid === data.guid) {
363
                    credential = local_credentials[i];
364
                    credential_index = i;
365
                    break;
366
                }
367
            }
368
        }
369
        credential.username = data.username;
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
370
        credential.password = data.password;
371
        credential.url = sender.tab.url;
372
        if (credential.guid !== null) {
373
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
374
                if (credential_index) {
375
                    local_credentials[credential_index] = updatedCredential;
376
                }
377
                saveMinedCallback({credential: credential, updated: true, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
378
                delete mined_data[sender.tab.id];
379
            });
380
        } else {
381
            credential.label = sender.tab.title;
382
            credential.vault_id = local_vault.vault_id;
383
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
384
                saveMinedCallback({credential: credential, updated: false, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
385
                local_credentials.push(createdCredential);
386
                delete mined_data[sender.tab.id];
387
            });
388
        }
389
    }
390
391
    _self.saveMined = saveMined;
392
393
    function searchCredential(searchText) {
394
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
395
        var results = [];
396
        for (var i = 0; i < local_credentials.length; i++) {
397
            var credential = local_credentials[i];
398
            for (var f = 0; f < searchFields.length; f++) {
399
                var field = searchFields[f];
400
                if (credential[field] && credential[field].indexOf(searchText) !== -1) {
401
                    results.push(credential);
402
                    break;
403
                }
404
            }
405
        }
406
        return results;
407
    }
408
409
    _self.searchCredential = searchCredential;
410
411
412
    function injectCreateCredential(args, sender) {
413
        var credential = PAPI.newCredential();
414
        credential.label = args.label;
415
        credential.username = args.username;
416
        credential.password = args.password;
417
        credential.vault_id = local_vault.vault_id;
418
        credential.url = sender.tab.url;
419
        PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
420
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
421
            local_credentials.push(createdCredential);
422
423
        });
424
    }
425
426
    _self.injectCreateCredential = injectCreateCredential;
427
428
    function saveCredential(credential) {
429
        if (!credential.credential_id) {
430
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
431
                local_credentials.push(createdCredential);
432
            });
433
        } else {
434
            var credential_index;
435
            for (var i = 0; i < local_credentials.length; i++) {
436
                if (local_credentials[i].guid === credential.guid) {
437
                    credential_index = i;
438
                    break;
439
                }
440
            }
441
442
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
443
                if (credential_index) {
444
                    local_credentials[credential_index] = updatedCredential;
445
                }
446
            });
447
        }
448
    }
449
450
    _self.saveCredential = saveCredential;
451
452
    function isVaultKeySet() {
453
        return (_self.settings.vault_password !== null);
454
    }
455
456
    _self.isVaultKeySet = isVaultKeySet;
457
458
    function isAutoFillEnabled() {
459
        if (!_self.settings.hasOwnProperty('disableAutoFill')) {
460
            return true;
461
        }
462
        return (_self.settings.disableAutoFill === false);
463
    }
464
465
    _self.isAutoFillEnabled = isAutoFillEnabled;
466
467
    var doorhangerData = null;
468
469
    function setDoorhangerData(data) {
470
        doorhangerData = data;
471
    }
472
473
    _self.setDoorhangerData = setDoorhangerData;
474
475
    function getDoorhangerData() {
476
        return doorhangerData;
477
    }
478
479
    _self.getDoorhangerData = getDoorhangerData;
480
481
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
482
483
        if (!msg || !msg.hasOwnProperty('method')) {
484
            return;
485
        }
486
        var result = false;
487
        if (_self[msg.method]) {
488
            result = _self[msg.method](msg.args, sender);
489
        } else {
490
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
491
        }
492
493
        sendResponse(result);
494
    });
495
496
    var defaultColor = '#0082c9';
497
498
    function createIconForTab(tab) {
499
        if (!master_password) {
500
            return;
501
        }
502
        var tabUrl = tab.url;
503
        var logins = getCredentialsByUrl(tabUrl);
504
        if (tab.active) {
505
            window.contextMenu.setContextItems(logins);
506
        }
507
        var credentialAmount = logins.length;
508
        API.browserAction.setBadgeText({
509
            text: credentialAmount.toString(),
510
            tabId: tab.id
511
        });
512
        API.browserAction.setBadgeBackgroundColor({
513
            color: defaultColor,
514
            tabId: tab.id
515
        });
516
517
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
518
        API.browserAction.setTitle({
519
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
520
            tabId: tab.id
521
        });
522
    }
523
524
    function displayLogoutIcons() {
525
        if (_self.settings) {
526
            API.tabs.query({}).then(function (tabs) {
527
                for (var t = 0; t < tabs.length; t++) {
528
                    var tab = tabs[t];
529
                    API.browserAction.setBadgeText({
530
                        text: '🔑',
531
                        tabId: tab.id
532
                    });
533
                    API.browserAction.setBadgeBackgroundColor({
534
                        color: '#ff0000',
535
                        tabId: tab.id
536
                    });
537
                    API.browserAction.setTitle({
538
                        title: API.i18n.getMessage('browser_action_title_locked'),
539
                        tabId: tab.id
540
                    });
541
                }
542
            });
543
        }
544
    }
545
546
    function updateTabsIcon() {
547
        API.tabs.query({}).then(function (tabs) {
548
            for (var t = 0; t < tabs.length; t++) {
549
                var tab = tabs[t];
550
                createIconForTab(tab);
551
            }
552
        });
553
    }
554
555
556
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
557
        if (master_password) {
558
            createIconForTab(tab);
559
        } else {
560
            displayLogoutIcons();
561
        }
562
    });
563
564
    API.tabs.onActivated.addListener(function () {
565
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
566
            if (master_password) {
567
                createIconForTab(tabs[0]);
568
            } else {
569
                displayLogoutIcons();
570
            }
571
        });
572
    });
573
574
    displayLogoutIcons();
575
576
    storage.get('master_password').then(function (password) {
577
        if (password) {
578
            master_password = password;
579
            API.api.browserAction.setBadgeBackgroundColor({
580
                color: defaultColor
581
            });
582
        }
583
        getSettings();
584
    }).error(function (error) {
585
        if (error === "Data not found") {
586
            getSettings();
587
        }
588
    });
589
    return _window;
590
}());
591
592